module mac(
    input [7:0] tx_up_us_i,
    input [7:0] tx_down_us_i,
    input [31:0] sync_word_i,
    input [1:0] rate_i,
    input [7:0] tx_payload_length_i,
    input [23:0] crc_init_i,
    input [6:0] whiten_init_i,
    input whiten_bypass_i,
    input tx_fsm_en_i,
    input tx_fsm_stop_i,
    input [31:0] start_time_i,
    input [31:0] target_time1_i,
    input [8:0] rx_up_us_i,
    input rx_fsm_en_i,
    input rx_fsm_stop_i,
    input [16:0] int_mask1_i,
    input fifo_dir_i,
    input fifo_clr_i,
    input [7:0] fifo_tx_threshold_i,
    input [7:0] fifo_rx_threshold_i,
    input [127:0] aes_data_i,
    input aes_en_i,
    input aes_start_i,
    input [7:0] aes_head_mask_i,
    input [127:0] aes_key_i,
    input [31:0] target_time2_i,
    input [16:0] int_mask2_i,
    input fifo_tx_push_i,
    input [7:0] fifo_tx_wr_data_i,
    input fifo_rx_pop_i,
    input [16:0] int_clr_req_i,
    output reg [16:0] int_raw_o,
    output [16:0] int_stat1_o,
    output [16:0] int_stat2_o,
    output [7:0] fifo_rx_rd_data_o,
    output [6:0] fifo_lvl_o,
    output reg [31:0] current_time_o,
    output reg [31:0] rx_time_o,
    output [23:0] crc_rslt_o,
    output mic_error_o,
    output [127:0] aes_rslt_o,
    output tx_en_o,
    output tx_data_o,
    output tx_data_valid_o,
    output rx_en_o,
    output [31:0] rx_sync_word_o,
    input rx_data_i,
    input rx_data_valid_i,
    input sync_found_i,
    output irq1_o,
    output irq2_o,
    input clk,
    input rst_n
);

assign irq1_o = |int_stat1_o;
assign irq2_o = |int_stat2_o;
assign rx_sync_word_o = sync_word_i;
wire [7:0] tx_byte;
wire tx_fifo_pop;
wire tx_start;
wire tx_aes_ccm_start;
wire [7:0] tx_unencrypted_byte;
wire [7:0] tx_unencrypted_byte_cnt;
wire tx_unencrypted_byte_update;
wire [7:0] tx_ccm_aad;
wire tx_encrypted_byte_valid;
wire [31:0] tx_mic;
wire [7:0] tx_encrypted_byte;
wire tx_crc_data;
wire tx_crc_en;
wire [23:0] tx_crc;
wire tx_unwhitened_data;
wire tx_whiten_shift;
wire tx_whitened_data;
tx_fsm tx_fsm_inst(
    .aa_i(sync_word_i),
    .tx_rate_i(rate_i),
    .whiten_bypass_i(whiten_bypass_i),
    .tx_up_us_i(tx_up_us_i),
    .tx_down_us_i(tx_down_us_i[4:0]),
    .tx_byte_i(tx_byte),
    .tx_payload_length_i(tx_payload_length_i),
    .aes_ccm_en_i(aes_en_i),
    .tx_fifo_pop_o(tx_fifo_pop),
    .start_i(tx_start),
    .tx_en_o(tx_en_o),
    .tx_data_valid_o(tx_data_valid_o),
    .tx_data_o(tx_data_o),

    .aes_ccm_start_o(tx_aes_ccm_start),
    .unencrypted_byte_o(tx_unencrypted_byte),
    .unecnrypted_byte_cnt_o(tx_unencrypted_byte_cnt),
    .unencrypted_byte_update_o(tx_unencrypted_byte_update),
    .ccm_aad_o(tx_ccm_aad),
    .tx_mic_i(tx_mic),
    .encrypted_byte_valid_i(tx_encrypted_byte_valid),
    .encrypted_byte_i(tx_encrypted_byte),

    .crc_data_o(tx_crc_data),
    .tx_crc_en_o(tx_crc_en),
    .tx_crc_i(tx_crc),

    .unwhitened_data_o(tx_unwhitened_data),
    .tx_whiten_shift_o(tx_whiten_shift),
    .whitened_data_i(tx_whitened_data),

    .clk(clk),
    .rst_n(rst_n&&!tx_fsm_stop_i)
);

wire [7:0] rx_byte;
wire rx_fifo_push;
wire rx_start;
wire [7:0] rx_payload_length;
wire rx_aes_ccm_start;
wire [7:0] rx_encrypted_byte;
wire [7:0] rx_encrypted_byte_cnt;
wire rx_encrypted_byte_update;
wire [7:0] rx_ccm_aad;
wire rx_decrypted_byte_valid;
wire [31:0] rx_mic_calc;
wire [7:0] rx_decrypted_byte;
wire rx_crc_data;
wire rx_crc_en;
wire [23:0] rx_crc_calc;
wire rx_whitened_data;
wire rx_whiten_shift;
wire rx_dewhitened_data;
rx_fsm rx_fsm_inst(
    .rx_rate_i(rate_i),
    .dewhiten_bypass_i(whiten_bypass_i),
    .rx_up_us_i(rx_up_us_i[7:0]),
    .rx_byte_o(rx_byte),
    .rx_crc_rslt_o(crc_rslt_o),
    .rx_crc_raw_o(),
    .rx_mic_err_o(mic_error_o),
    .aes_ccm_en_i(aes_en_i),
    .rx_fifo_push_o(rx_fifo_push),
    .start_i(rx_start),
    .rx_en_o(rx_en_o),
    .rx_data_i(rx_data_i),
    .rx_data_valid_i(rx_data_valid_i),
    .sync_found_i(sync_found_i),

    .rx_payload_length_o(rx_payload_length),
    .aes_ccm_start_o(rx_aes_ccm_start),
    .encrypted_byte_o(rx_encrypted_byte),
    .encrypted_byte_cnt_o(rx_encrypted_byte_cnt),
    .encrypted_byte_update_o(rx_encrypted_byte_update),
    .ccm_aad_o(rx_ccm_aad),
    .decrypted_byte_valid_i(rx_decrypted_byte_valid),
    .rx_mic_calc_i(rx_mic_calc),
    .decrypted_byte_i(rx_decrypted_byte),

    .crc_data_o(rx_crc_data),
    .rx_crc_en_o(rx_crc_en),
    .rx_crc_calc_i(rx_crc_calc),

    .whitened_data_o(rx_whitened_data),
    .rx_whiten_shift_o(rx_whiten_shift),
    .dewhitened_data_i(rx_dewhitened_data),

    .clk(clk),
    .rst_n(rst_n&&!rx_fsm_stop_i)
);

wire [23:0] crc_o;
assign tx_crc = crc_o;
assign rx_crc_calc = crc_o;
crc crc_inst(
    .init_i(crc_init_i),
    .update_init_i(tx_start||rx_start),
    .data_i(tx_en_o?tx_crc_data:rx_crc_data),
    .en_i(tx_en_o?tx_crc_en:rx_crc_en),
    .clk(clk),
    .rst_n(rst_n&&!tx_fsm_stop_i&&!rx_fsm_stop_i),
    .crc_o(crc_o)
);

wire whiten_data_o;
assign tx_whitened_data = whiten_data_o;
assign rx_dewhitened_data = whiten_data_o;
whiten whiten_inst(
    .init_i(whiten_init_i),
    .update_init_i(tx_start||rx_start),
    .data_i(tx_en_o?tx_unwhitened_data:rx_whitened_data),
    .shift_i(tx_en_o?tx_whiten_shift:rx_whiten_shift),
    .clk(clk),
    .rst_n(rst_n&&!tx_fsm_stop_i&&!rx_fsm_stop_i),
    .data_o(whiten_data_o)
);

wire [7:0] ccm_byte_o;
wire [31:0] ccm_mic_o;
wire aes_enc_done;
wire ccm_byte_out_valid;
assign tx_encrypted_byte_valid = ccm_byte_out_valid;
assign rx_decrypted_byte_valid = ccm_byte_out_valid;
assign tx_encrypted_byte = ccm_byte_o;
assign rx_decrypted_byte = ccm_byte_o;
assign tx_mic = ccm_mic_o;
assign rx_mic_calc = ccm_mic_o;
wire [7:0] ccm_payload_length = tx_en_o?(tx_payload_length_i-4):(rx_payload_length-4);
aes_ccm aes_ccm_inst(
    .nonce_i({aes_data_i[127:64],aes_data_i[39:0]}),
    .key_i(aes_key_i),
    .aad_i((tx_en_o?tx_ccm_aad:rx_ccm_aad)&aes_head_mask_i),
    .payload_length_i(ccm_payload_length),
    .ccm_start_i(tx_en_o?tx_aes_ccm_start:rx_aes_ccm_start),
    .byte_i(tx_en_o?tx_unencrypted_byte:rx_encrypted_byte),
    .byte_cnt_i(tx_en_o?tx_unencrypted_byte_cnt:rx_encrypted_byte_cnt),
    .byte_update_i(tx_en_o?tx_unencrypted_byte_update:rx_encrypted_byte_update),
    .byte_out_valid_o(ccm_byte_out_valid),
    .byte_o(ccm_byte_o),
    .mic_o(ccm_mic_o),
    .aes_data_i(aes_data_i),
    .aes_rslt_o(aes_rslt_o),
    .aes_start_i(aes_start_i),
    .aes_enc_done_o(aes_enc_done),
    .enc_i(tx_en_o),
    .clk(clk),
    .rst_n(rst_n&&!tx_fsm_stop_i&&!rx_fsm_stop_i)
);

wire [7:0] fifo_pop_data_o;
assign tx_byte = fifo_pop_data_o;
assign fifo_rx_rd_data_o = fifo_pop_data_o;
wire fifo_full;
wire fifo_empty;
wire fifo_underflow;
wire fifo_overflow;
sync_fifo #(
    .WIDTH(8),
    .DEPTH_POWER(6)
) sync_fifo_inst(
    .push_en_i(fifo_dir_i==0?fifo_tx_push_i:rx_fifo_push),
    .pop_en_i(fifo_dir_i==0?tx_fifo_pop:fifo_rx_pop_i),
    .push_data_i(fifo_dir_i==0?fifo_tx_wr_data_i:rx_byte),
    .pop_data_o(fifo_pop_data_o),
    .level_o(fifo_lvl_o),
    .full_o(fifo_full),
    .empty_o(fifo_empty),
    .overflow_o(fifo_overflow),
    .underflow_o(fifo_underflow),
    .clk(clk),
    .rst_n(rst_n&&!fifo_clr_i)
);

always @(posedge clk,negedge rst_n) begin
    if(!rst_n) current_time_o <= 0;
    else current_time_o <= current_time_o + 1;
end

always @(posedge clk,negedge rst_n) begin
    if(!rst_n) rx_time_o <= 0;
    else if(sync_found_i) rx_time_o <= current_time_o;
end

assign tx_start = tx_fsm_en_i ? start_time_i == current_time_o: 0;
assign rx_start = rx_fsm_en_i ? start_time_i == current_time_o: 0;
assign int_stat1_o = int_raw_o & int_mask1_i;
assign int_stat2_o = int_raw_o & int_mask2_i;
always @(posedge clk,negedge rst_n) begin
    if(!rst_n) int_raw_o <= 0;
    else if(int_clr_req_i) int_raw_o <= int_raw_o & ~int_clr_req_i;
    else begin
        if(fifo_underflow) int_raw_o[3] <= 1;
        if(sync_found_i) int_raw_o[5] <= 1;
        if(fifo_overflow) int_raw_o[9] <= 1;
        if(target_time1_i == current_time_o) int_raw_o[10] <= 1;
        if(fifo_dir_i == 0 && fifo_lvl_o < fifo_tx_threshold_i) int_raw_o[12] <= 1;
        if(fifo_dir_i == 1 && fifo_lvl_o > fifo_rx_threshold_i) int_raw_o[13] <= 1;
        if(target_time2_i == current_time_o) int_raw_o[15] <= 1;
        if(aes_enc_done) int_raw_o[16] <= 1;
    end
end

endmodule